/*
 * Copyright 2007 Jaisy Sprl.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package be.jaisy.ui.gwt.widget.client.viewer.table.internal;

import java.util.Arrays;
import java.util.Comparator;

import be.jaisy.ui.gwt.widget.client.viewer.ContentViewer;
import be.jaisy.ui.gwt.widget.client.viewer.IBaseLabelProvider;
import be.jaisy.ui.gwt.widget.client.viewer.table.ITableLabelProvider;

/**
 * A Table viewer comparator is used to reorder the elements provided by its
 * content provider.
 * <p>
 * The default <code>compare</code> method compares elements using a case
 * insensitive compare of the strings obtained from the content viewer's label
 * provider via <code>IBaseLabelProvider.getText</code> or
 * <code>ITableLabelProvider.getText(int columnIndex)</code>.
 * </p>
 * 
 * @author Stany Blanvalet <stany.blanvalet@jaisy.be>
 */
public class TableViewerComparator {
	/**
	 * The comparator to use to sort a viewer's contents.
	 */
	private Comparator comparator;

	/**
	 * Creates a new {@link TableViewerComparator}, which uses the default
	 * comparator to sort strings.
	 * 
	 */
	public TableViewerComparator() {
		this(getDefaultComparator());
	}

	/**
	 * Creates a new {@link TableViewerComparator}, which uses the given
	 * comparator to sort strings.
	 * 
	 * @param comparator
	 */
	public TableViewerComparator(Comparator comparator) {
		this.comparator = comparator;
	}

	/**
	 * Returns the comparator used to sort strings.
	 * 
	 * @return the comparator used to sort strings
	 */
	protected Comparator getComparator() {
		return comparator;
	}

	/**
	 * Returns a negative, zero, or positive number depending on whether the
	 * first element is less than, equal to, or greater than the second element.
	 * 
	 * @param StructuredTableViewer
	 *            the table viewer
	 * @param e1
	 *            the first element
	 * @param e2
	 *            the second element
	 * @return a negative number if the first element is less than the second
	 *         element; the value <code>0</code> if the first element is equal
	 *         to the second element; and a positive number if the first element
	 *         is greater than the second element
	 */
	public int compare(ContentViewer viewer, Object e1, Object e2, int columnIndex) {

		String name1;
		String name2;

		if (viewer == null) {
			name1 = e1.toString();
			name2 = e2.toString();
		} else {
			IBaseLabelProvider prov = viewer.getLabelProvider();
			if (prov != null && prov instanceof ITableLabelProvider) {
				Object[] cell1s = ((ITableLabelProvider) prov).getRow(e1);
				Object[] cell2s = ((ITableLabelProvider) prov).getRow(e2);
				
				Object cell1 = columnIndex < cell1s.length ? cell1s[columnIndex] : null;
				Object cell2 = columnIndex < cell2s.length ? cell2s[columnIndex] : null;				
				
				name1 = (cell1 instanceof String) ? (String)cell1 : cell1.toString();
				name2 = (cell2 instanceof String) ? (String)cell2 : cell2.toString();
			} else {
				name1 = e1.toString();
				name2 = e2.toString();
			}
		}
		if (name1 == null) {
			name1 = "";//$NON-NLS-1$
		}
		if (name2 == null) {
			name2 = "";//$NON-NLS-1$
		}

		// use the comparator to compare the strings
		return getComparator().compare(name1, name2);
	}

	/**
	 * Sorts the given elements in-place, modifying the given array.
	 * <p>
	 * The default implementation of this method uses the java.util.Arrays#sort
	 * algorithm on the given array, calling <code>compare</code> to compare
	 * elements.
	 * </p>
	 * <p>
	 * Subclasses may reimplement this method to provide a more optimized
	 * implementation.
	 * </p>
	 * 
	 * @param StructuredTableViewer
	 *            the table viewer
	 * @param elements
	 *            the elements to sort
	 */
	public void sort(final ContentViewer viewer, Object[] elements, final int columnIndex) {
		Arrays.sort(elements, new Comparator() {
			public int compare(Object a, Object b) {
				return TableViewerComparator.this.compare(viewer, a, b, columnIndex);
			}
		});
	}

	/**
	 * UnSorts the given elements in-place, modifying the given array.
	 * <p>
	 * The default implementation of this method uses the java.util.Arrays#sort
	 * algorithm on the given array, calling <code>compare</code> to compare
	 * elements.
	 * </p>
	 * <p>
	 * Subclasses may reimplement this method to provide a more optimized
	 * implementation.
	 * </p>
	 * 
	 * @param StructuredTableViewer
	 *            the table viewer
	 * @param elements
	 *            the elements to unsort
	 */
	public void unsort(final ContentViewer viewer, Object[] elements, final int columnIndex) {
		Arrays.sort(elements, new Comparator() {
			public int compare(Object a, Object b) {
				return -1 * TableViewerComparator.this.compare(viewer, a, b, columnIndex);
			}
		});
	}

	/**
	 * Return the default comparator used by JFace to sort strings.
	 * 
	 * @return a default comparator used by JFace to sort strings
	 */
	private static Comparator getDefaultComparator() {
		return new Comparator() {
			/**
			 * Compares string s1 to string s2.
			 * 
			 * @param s1
			 *            string 1
			 * @param s2
			 *            string 2
			 * @return Returns an integer value. Value is less than zero if
			 *         source is less than target, value is zero if source and
			 *         target are equal, value is greater than zero if source is
			 *         greater than target.
			 * @exception ClassCastException
			 *                the arguments cannot be cast to Strings.
			 */
			public int compare(Object s1, Object s2) {
				return ((String) s1).compareTo((String) s2);
			}
		};
	}
}